<?php
/**
* Class d'abstraction de base de donnée
* Liste les colonnes d'une table de donnée puis crée une variable par colonne
* @author: Joseph Lemoine - lemoine.joseph@gmail.com
* @version: 11
*/
class Dao {
    /**
     * Liste des objet _PDO_Prepare
     * @var	array	[$table] = PDO::Prepare)
     */
    protected static $_PDO_DaoSave = array();

    /**
     * Liste des requète déjà récupéré
     * @var	array	[$table] = array() requète récupéré
     */
    protected static $_Dao_Loaded = array();

    /**
     * Liste des colonnes de la table
     * @var	array   [table][Numéro] = $nom
     */
    protected static $_colist;

    /**
     * Nom de la base de donnée liée
     * @var     string
     */
    protected $_bdd = "ICU";

    /**
     * Nom de la table
     * @var     string
     */
    protected $_table = "";

    /**
     * Définie l'accès en lecture seule ou non de la table
     * @var	    bool
     */
    protected $_readOnly = false;

    /**
     * Nom de cette class, utilisé pour créer à la volée
     * cette instance sans réecrire son nom partout
     * @var:    string
     */
    protected $_nomClass = __CLASS__;

    /**
     * Constructeur de class
     * Récupère et crée les variables de la table associé.
     * @param	string	$bdd	Nom de la base de donnée à lier
     * @param	string	$table   Nom de la table à lister
     * @param	bool	$readOnly	Si cette table est en lecture seule
     * @return	bool	true si le listing est ok, sinon false;
     * @access	public
     */
    public function __construct($base = "", $table = "", $readOnly = FALSE) {
		/* Définie si cette table est en lecture seule ou non */
		$this->_readOnly = (empty($this->_readOnly) ? $readOnly : $this->_readOnly);

		/* Définie le nom de la base de donnée si il n'est pas forcé par la class*/
		$this->_bdd = (empty($this->_bdd) ? $base : $this->_bdd);

		/* Définie le nom de la table si il n'est pas forcé par la class */
		$this->_table = (empty($this->_table) ? $table : $this->_table);

		/* Si des tables ont bien étés spécifés */
		if(!empty($this->_table)) {
			/* Récupération du nom des colonnes et création des variables */
			$this->extractCols();

			return true;
		}else{
			return false;
		}
    }

    /**
     * Extrait les colonnes de la table nommée
     * @param	string	$table  Nom de la table à lister
     * @return	bool		true si le listing est ok, sinon false. Les
     *				données sont indexés dans $_colist
     * @access	protected
     */
    protected function extractCols() {
		/* Si on à pas encore créer une instance de cette table, on liste ses colonnes*/
		if(empty(self::$_colist[$this->_bdd . "." . $this->_table])){

			/* Envois de la requète au singleton SQL */
			$colist = Sql::getInstance($this->_bdd)->query("SHOW COLUMNS FROM " . $this->_table)->fetchAll();

			/* Initialisation du nombre de colonnes*/
			$nbCol = 0;

			/* Listing des colonnes */
			foreach($colist AS $data){
			/* Création de la variable dans le DAO */
			$this->$data[0] = null;

			/* Liste des variable récupéré */
			self::$_colist[$this->_bdd . "." . $this->_table][$nbCol] = $data[0];
			$nbCol++;
			}
		}else{
			/* Sinon on crée les variables */
			foreach(self::$_colist[$this->_bdd . "." . $this->_table] AS $row => $name)
			$this->$name = null;
		}

    }

    /**
     * Méthode de récuparation des valeurs d'une ligne
     * Sauvegarde de l'adresse de la ligne dans une variable pour
     * la passe d'exportation lors de la destruction de la class
     * Les données récupérés sont stockés afin de ne pas refaire une requète si
     * le même identifiant est demandé plus tard dans le code (sauf rechargement
     * forcé)
     * @Param	int	    $id		    Identifiant de la ligne à récupérer
     * @param	string	    $column	    Nom de la colonne où cherche l'ID
     * @param	string	    $column	    Nom de la colonne où effectuer un tri
     * @param	bool	    $forceReload    Force le rechargement des données
     * @param	string		$selector	Ligne de tri suplémentaire
     * @return	bool
     * @access	public
     */
    public function load($id = null, $column = null, $order = null, $forceReload = false, $selector = null) {

		/* Nom de la colonne à appeller si on en à pas spécifier une */
		$column = (empty($column) ? self::$_colist[$this->_bdd . "." . $this->_table][0] : $column);

		/* Nom de la colonne à trier si on en à pas spécifier une */
		$order = (empty($order) ? self::$_colist[$this->_bdd . "." . $this->_table][0] : $order);

		/* Si on à spécifié un ID, on liste les valeurs de cette ligne */
		if($id){
			/* Si on à déjà exécuté cette requète */
			if(empty(self::$_Dao_Loaded[$this->_bdd . "." . $this->_table][$id]) || $forceReload){
				/* Ecriture de la requète */
				$sql = "SELECT ";
				/* Listing des colonnes pour le pas utiliser (*) */
				foreach(self::$_colist[$this->_bdd . "." . $this->_table] AS $stuff => $name)
					$sql .= "t." . $name . ", ";

				/* On recherche l'occurence avec l'id étant la première colonne listé */
				$sql = substr($sql, 0, -2)." "
					."FROM ".$this->_table." AS t "
					."WHERE t.".$column." = "
						.Sql::getInstance($this->_bdd)->quote($id, PDO::PARAM_STR)." "
						.$selector." "
					."ORDER BY t.".$order." DESC LIMIT 0,1";

				/* Exécution et récupération de la ligne*/
				self::$_Dao_Loaded[$this->_bdd . "." . $this->_table][$id] = Sql::getInstance($this->_bdd)->query($sql)->fetch();
			}

			if(!empty(self::$_Dao_Loaded[$this->_bdd . "." . $this->_table][$id])){
				/* Injection des valeurs des variables de l'objet */
				foreach(self::$_colist[$this->_bdd . "." . $this->_table] AS $position => $name){
					$this->$name = self::$_Dao_Loaded[$this->_bdd . "." . $this->_table][$id][$name];
				}

				/* On renvoit vraie car tout est bien exécuté */
				return TRUE;
			} else {
				/* Définition de la valeur de référence malgrés tout */
				$this->$column = $id;
				/* On renvoit faux car pas de valeur retour */
				return FALSE;
			}
		}else{
			/* Si on à pas d'ID on s'en tape l'objet se vera
			 * certainement attribuer ses paramètres ^_^ */
			return TRUE;
		}
    }

    /**
     * Méthode d'effacement d'une ligne
     * @param	$id	int	Identifiant de la ligne à effacer
     * @return	bool	TRUE	Action effectuée
     * @access	public
     */
    public function delete($id = NULL) {
		/* Si c'est objet n'est pas en lecture seule */
		if(!$this->_readOnly){
			/* On suppprime cette ligne */
			if(empty($id))
				$idToDel = $this->id;
			else
				$idToDel = $id;

			$sql = "DELETE FROM " . $this->_table
				. " WHERE " . self::$_colist[$this->_bdd . "." . $this->_table][0] . " = '" . $idToDel . "'";

			/* Exécution */
			Sql::getInstance($this->_bdd)->query($sql);

			/* Si on à pas spécifié d'ID, c'est qu'on supprime cet objet */
			if(empty($id)) {
				/* On lock */
				$this->_readOnly = TRUE;
				/* Cet objet ne sera pas sauvegardé */
				unset($this);
			}

		    return TRUE;
		}else{
		    /* Sinon, c'est que cet objet est en lecture seule, on n'y touche pas ! */
		    return FALSE;
		}
    }

    /**
     * Méthode static pour effacer une ligne
     * @param	int	$id	Identifiant de la ligne à effacer
     * @return	bool
     * @access	public
     * @static
     */
    public function del($id = NULL) {
    	if(!empty($id)) {
    		/* Ecriture de la requète */
    		$sql = "DELETE FROM " . self::$_table
				. " WHERE " . self::$_colist[self::$_bdd . "." . self::$_table][0] . " = '" . $id . "'";

			/* Exécution */
			if(Sql::getInstance($this->_bdd)->query($sql))
				return true;
			else
				return false;
    	} else {
    		return false;
    	}
    }

    /**
     * Création d'une requète préparé pour sauvegarder les enfants de Dao
     * @return objet   PDO::Prepare
     * @access protected
     */
    protected function prepareSauvegarde() {

		/* Création de la requète préparé de sauvegarde */
		$sql = "REPLACE INTO " . $this->_table . " VALUES (";

		/* Listing des variables dans l'ordre */
		foreach(self::$_colist[$this->_bdd . "." . $this->_table] AS $row => $name) {
		    /* Pour la requète de préparation */
		    $sql .= ":" . $name . ", ";
		}

		/* Fin d'écriture de la requète */
		$sql = substr($sql, 0, -2) . ");";

		return Sql::getInstance($this->_bdd)->prepare($sql);

    }

    /**
     * Liste les variables de l'objet dans un tableau
     * Syntaxe faite pour PDO::Prepare
     * @return: array   (nomVariable => valeur)
     * @access: protected
     */
    protected function listeVariable() {

		/* Listing des variables dans l'ordre */
		foreach(self::$_colist[$this->_bdd . "." . $this->_table] AS $row => $name) {
		    /* Données à envoyer */
		    $liste[":" . $name] = $this->$name;
		}

		return $liste;
    }

    /**
     * Fonction de sauvegarde des objets ayant été chargé / créer avec load()
     * SEULEMENT SI L'OBJET N'EST PAS EN LECTURE SEULE !
     * Ou qu'on force la sauvegarde avec le paramètre
     * @param	bool	$forceSave  Si true, force la sauvegarde
     * @return	int	Nombre d'enregistrements
     * @access	protected
     */
    protected function save($forceSave = FALSE) {

		/* Si cet objet n'est pas en lecture seule */
		if(!$this->_readOnly || $forceSave){
			/* Création de la requète préparé si ce n'est pas déjà fait */
			if(!self::$_PDO_DaoSave[$this->_bdd . "." . $this->_table])
				self::$_PDO_DaoSave[$this->_bdd . "." . $this->_table] = $this->prepareSauvegarde();

			/* Sauvegarde de lui même */
			return self::$_PDO_DaoSave[$this->_bdd . "." . $this->_table]->execute($this->listeVariable());;

		}else{
			/* Sinon on n'y touche pas ! */
			return FALSE;
		}

    }

    /**
     * Fonction de sauvegarde publique, force l'enregistrement même en lecture seule
     * @return	int	Nombre d'enregistrements
     * @access	public
     */
    public function store(){
		$entry = $this->save(TRUE);

		/* Si on a pas réussit la sauvegarde */
		if(empty($entry)){
			echo "ERREUR : Impossible de sauver cet objet !<br />";
			return FALSE;
		/* Si c'est une réussite */
		}else{
			/* Récupération de la variable de référence */
			$rowOfReference = self::$_colist[$this->_bdd . "." . $this->_table][0];

			/* Récupération de la dernière ID inséré */
			$lastInsertId = Sql::getInstance($this->_bdd)->lastInsertId();

			/* Si on en à bien une, on donne cette valeur à notre variable de référence */
			if(!empty($lastInsertId))
				$this->$rowOfReference = Sql::getInstance($this->_bdd)->lastInsertId();

			/* On renvoit le nombre d'enregistrement (ici 1, donc TRUE) */
			return $entry;
		}
    }


    /**
     * Destructeur de class, envoie les instructions de sauvegarde
     * @return: bool    TRUE
     * @access: public
     */
    public function  __destruct() {
		/* Si l'objet n'est pas en lecture seule uniquement */
		if(!$this->_readOnly){
		    /* On le sauvegarde */
		    $this->save();
		}
    }

    /**
     * ===================================================================
     * =========================     DEBUG    ============================
     * ===================================================================
     */

    /**
     * Fonction destiné au DEBUG
     * permet d'afficher la liste des variable et leur valeur courante
     * @return	string
     * @access	public
     */
    public function showName() {
		$send = "table " . $this->_table . ", valeurs courante de la ligne "
		    . self::$_colist[$this->_bdd . "." . $this->_table][0] . " :<br />";

		/* Liste des variable avec leur valeur */
		foreach(self::$_colist[$this->_bdd . "." . $this->_table] AS $row => $name) {
		    $send .= $name . " : " . $this->$name . "<br />";
		}

		return $send;
    }

}
?>